package com.dgz.csmall.passport.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.dgz.csmall.commons.pojo.vo.PageData;
import com.dgz.csmall.commons.web.ServiceCode;
import com.dgz.csmall.commons.ex.ServiceException;
import com.dgz.csmall.passport.dao.persist.mapper.AdminMapper;
import com.dgz.csmall.passport.dao.persist.mapper.AdminRoleMapper;
import com.dgz.csmall.passport.pojo.entity.Admin;
import com.dgz.csmall.passport.pojo.entity.AdminRole;
import com.dgz.csmall.passport.pojo.param.AdminAddNewParam;
import com.dgz.csmall.passport.pojo.param.AdminLoginInfoParam;
import com.dgz.csmall.passport.pojo.vo.AdminListItemVO;
import com.dgz.csmall.passport.security.AdminDetails;
import com.dgz.csmall.passport.service.AdminService;
import com.dgz.csmall.commons.util.PageInfoToPageDataConverter;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;

/**
 * @Author duguozheng
 * @Date 2023/05/18  16:50
 * @Description 管理员业务实现类
 * @Version 1.0
 */
@Slf4j
@Service
public class AdminServiceImpl implements AdminService {

    @Value("${csmall.jwt.secret-key}")
    private String secretKey;

    @Value("${csmall.jwt.duration-in-minute}")
    private Long durationInMinute;

    @Autowired
    AdminMapper adminMapper;

    @Autowired
    private AdminRoleMapper adminRoleMapper;

    @Autowired
    private AuthenticationManager authenticationManager;

    public AdminServiceImpl() {
        log.debug("创建业务类对象：AdminServiceImpl");
    }

    @Override
    public String login(AdminLoginInfoParam adminLoginInfoParam) {
        log.debug("开始处理【管理员登录】的业务，参数：{}", adminLoginInfoParam);

        // 创建认证时所需的参数对象
        Authentication authentication = new UsernamePasswordAuthenticationToken(
                adminLoginInfoParam.getUsername(),
                adminLoginInfoParam.getPassword());

        // 执行认证，并获取认证结果
        Authentication authenticateResult = authenticationManager.authenticate(authentication);
        log.debug("验证登录完成！认证结果：{}", authenticateResult);

        // 从认证结果中取出所需的数据
        Object principal = authenticateResult.getPrincipal();
        AdminDetails adminDetails = (AdminDetails) principal;
        Collection<GrantedAuthority> authorities = adminDetails.getAuthorities();

        // 生成JWT
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", adminDetails.getId());
        claims.put("username", adminDetails.getUsername());
        claims.put("authoritiesJsonString", JSON.toJSONString(authorities));
        String jwt = Jwts.builder()
                // Header
                .setHeaderParam("alg", "HS256")
                .setHeaderParam("typ", "JWT")
                // Payload
                .setClaims(claims)
                .setExpiration(new Date(System.currentTimeMillis() + durationInMinute * 60 * 1000))
                // Verify Signature
                .signWith(SignatureAlgorithm.HS256, secretKey)
                // 生成
                .compact();
        log.debug("生成了此管理员的信息对应的JWT：{}", jwt);
        return jwt;

        // ===== 使用JWT后不再需要以下2行代码 =====
        // 将认证结果存入到SecurityContext中
        // SecurityContext securityContext = SecurityContextHolder.getContext();
        // securityContext.setAuthentication(authenticateResult);
    }
    @Override
    public void addNew(AdminAddNewParam adminAddNewParam) {
        log.debug("开始处理【添加管理员】业务，参数：{}", adminAddNewParam);

        //检查用户名是否被已存在，已存在则抛出异常
        QueryWrapper<Admin> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", adminAddNewParam.getUsername());
        Integer countByUsername = adminMapper.selectCount(queryWrapper);
        log.debug("根据用户名统计匹配的管理员数量，结果：{}", countByUsername);
        if (countByUsername > 0) {
            String message = "添加管理员失败，管理员已存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
        }

        // 检查管理员手机号码是否被占用，如果被占用，则抛出异常
        QueryWrapper<Admin> queryWrapper2 = new QueryWrapper<>();
        queryWrapper2.eq("phone", adminAddNewParam.getPhone());
        Integer countByPhone = adminMapper.selectCount(queryWrapper2);
        log.debug("根据手机号码统计匹配的管理员数量，结果：{}", countByPhone);
        if (countByPhone > 0) {
            String message = "添加管理员失败，该手机号码已存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
        }

        //检查管理员电子邮箱是否被占用，如果被占用，则抛出异常
        QueryWrapper<Admin> queryWrapper3 = new QueryWrapper<>();
        queryWrapper3.eq("email", adminAddNewParam.getEmail());
        Integer countByEmail = adminMapper.selectCount(queryWrapper3);
        log.debug("根据电子邮箱统计匹配的管理员数量，结果：{}", countByEmail);
        if (countByEmail > 0) {
            String message = "添加管理员失败，该电子邮箱已存在！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_CONFLICT, message);
        }

        //将管理员数据写入到数据库中
        Admin admin = new Admin();
        BeanUtils.copyProperties(adminAddNewParam, admin);
        admin.setLastLoginIp(null);
        admin.setLoginCount(0);
        admin.setGmtLastLogin(null);
        admin.setGmtCreate(LocalDateTime.now());
        admin.setGmtModified(LocalDateTime.now());
        int rows = adminMapper.insert(admin);
        if (rows != 1) {
            String message = "添加管理员失败，服务器忙，请稍后再试！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_INSERT, message);
        }
        log.debug("将新的管理员数据写入到数据库，完成！");

        //将管理员与角色的关联数据写入到数据库中
        Long[] roleIds = adminAddNewParam.getRoleIds();
        List<AdminRole> adminRoleList = new ArrayList<>();
        LocalDateTime timeNow = LocalDateTime.now();
        for (Long roleId : roleIds) {
            AdminRole adminRole = new AdminRole();
            adminRole.setAdminId(admin.getId());
            adminRole.setRoleId(roleId);
            adminRole.setGmtCreate(timeNow);
            adminRole.setGmtModified(timeNow);
            adminRoleList.add(adminRole);
        }
        rows = adminRoleMapper.insertBatch(adminRoleList);
        if (rows != adminRoleList.size()) {
            String message = "添加管理员失败，服务器忙，请稍后再试！";
            log.warn(message);
            throw new ServiceException(ServiceCode.ERR_INSERT, message);
        }
        log.debug("将新的管理员与角色的关联数据插入到数据库，完成！");
    }

    @Override
    public PageData<AdminListItemVO> list(Integer pageNum) {
        Integer pageSize = 5;
        return list(pageNum, pageSize);
    }

    @Override
    public PageData<AdminListItemVO> list(Integer pageNum, Integer pageSize) {
        log.debug("开始处理【查询管理员列表】业务，页码：{},每页记录数：{}", pageNum, pageSize);
        PageHelper.startPage(pageNum, pageSize);
        List<AdminListItemVO> list = adminMapper.list();
        PageInfo<AdminListItemVO> pageInfo = new PageInfo<>(list);
        PageData<AdminListItemVO> pageData = PageInfoToPageDataConverter.convert(pageInfo);
        log.debug("查询完成，即将返回：{}", pageData);
        return pageData;
    }
}
